最後,我們來到了 SOLID 當中的介面隔離原則。這裡我們先舉先前提到過的 BaseballPlayer and TennisPlayer 的例子。
由於兩個類別的運動選手,同樣都會擊球和跑奔跑,因此我們將這兩個方法抽象出來,變成一個 Athlete 介面
interface Athlete {
  hit(): void;
  run(): void;
}
接著,我們讓 BaseballPlayer and TennisPlayer 分別去執行這個介面。當然兩者各自有自己的實作方式
class BaseballPlayer implements Athlete {
  name: string
  
  constructor(name){
    this.name = name
  }
  hit() {
    console.log(`${this.name} can hit baseball`)
  }
  
  run() {
    console.log(`${this.name} can run`)
  }
}
class TennisPlayer implements Athlete {
  name: string
  
  constructor(name){
    this.name = name
  }
  hit() {
    console.log(`${this.name} can hit tennis`)
  }
  
  run() {
    console.log(`${this.name} can run`)
  }
}
最後,我們可以得到
const jeter = new BaseballPlayer('jeter')
const federer = new TennisPlayer('federer')
jeter.hit()    // jeter can hit baseball
jeter.run()    // jeter can run
federer.hit()  // federer can hit tennis
federer.run()  // federer can run
後來,BaseballPlayer 認為他自己除了會打會跑之外,還要很會投球,因此希望 Athlete 可以加入 pitch 這個方法。
interface Athlete {
  hit(): void;
  run(): void;
  pitch(): void;
}
於是 Athlete 從善如流
class BaseballPlayer implements Athlete {
  name: string
  
  constructor(name){
    this.name = name
  }
  hit() {
    console.log(`${this.name} can hit baseball`)
  }
  
  run() {
    console.log(`${this.name} can run`)
  }
  
  pitch() {
    console.log(`${this.name} can pitch`)
  }
}
BaseballPlayer 也順利實作出 pitch 方法
const jeter = new BaseballPalyer('jeter')
jeter.pitch()    // jeter can pitch
不過這時候 TennisPlayer 就覺得有點奇怪,因為他認為網球選手不需要會投球。但是因為同樣執行了 Athlete,因此需要實作 pitch 方法,所以該怎麼辦呢?
於是 TennisPlayer 只好硬著頭皮這樣實作
class TennisPlayer implements Athlete {
  name: string
  
  constructor(name){
    this.name = name
  }
  hit() {
    console.log(`${this.name} can hit tennis`)
  }
  
  run() {
    console.log(`${this.name} can run`)
  }
  
  pitch() {
    console.log(`Sorry ${this.name} cannot pitch`)
  }
}
然後告訴大家其實網球選手不會投球
const federer = new TennisPlayer('jeter')
federer.pitch()    // Sorry federer cannot pitch
這時候,介面隔離原則就說話了:
In the field of software engineering, the interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.
使用者不應該被強迫依賴不需要的功能。
回到剛剛的例子,就是不應該強迫 TennisPlayer 去執行 Athlete 的 pitch 功能。
但是BaseballPlayer還是需要 pitch 這個功能啊,該怎麼辦呢?其實很簡單,就把所介面給拆開吧!
interface Athlete {
  hit(): void;
  run(): void;
}
interface Pitch {
  pitch(): void;
}
然後各自執行自己需要的介面。像是 BaseballPlayer 就執行 Athlete 和 Pitch
class BaseballPlayer implements Athlete, Pitch {
  name: string
  
  constructor(name){
    this.name = name
  }
  hit() {
    console.log(`${this.name} can hit baseball`)
  }
  
  run() {
    console.log(`${this.name} can run`)
  }
  
  pitch() {
    console.log(`${this.name} can pitch`)
  }
}
而 TennisPlayer 只要執行 Athlete 就行
class TennisPlayer implements Athlete {
  name: string
  
  constructor(name){
    this.name = name
  }
  hit() {
    console.log(`${this.name} can hit tennis`)
  }
  
  run() {
    console.log(`${this.name} can run`)
  }
}
各取所需,打完收工!
「介面隔離原則」同樣從使用者的角度出發,不要依賴於完全不需要的功能,以避免未來收到不相關的功能影響。